package com.book.manager.service.authentication.impl;

import com.book.manager.base.result.CodeMsgEnum;
import com.book.manager.dto.LoginReq;
import com.book.manager.dto.Token;
import com.book.manager.exception.MyRuntimeException;
import com.book.manager.mapper.UserMapper;
import com.book.manager.model.User;
import com.book.manager.oauth2.AuthenticationFacade;
import com.book.manager.oauth2.ServerConfig;
import com.book.manager.security.MyUserDetailsService;
import com.book.manager.service.authentication.MyAuthenticationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;

/**
 * @author: yfan-chen
 * @description:
 * @create: 2020-03-21 21:46
 **/
@Service
public class MyAuthenticationServiceImpl implements MyAuthenticationService {

    Logger logger = LoggerFactory.getLogger(MyAuthenticationServiceImpl.class);

    @Value("${spring.security.oauth2.client.clientId}")
    private String clientId;
    @Value("${spring.security.oauth2.client.clientSecret}")
    private String clientSecret;
    @Value("${spring.security.oauth2.client.authorizedGrantTypes}")
    private String authorizedGrantTypes;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private MyUserDetailsService userDetailsService;
    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    TokenStore tokenStore;

    @Autowired
    private AuthenticationFacade authenticationFacade;


    @Override
    public Token loginWithUserNameAndPw(HttpServletRequest request, LoginReq req) {
        User User = null;
        try {
            if (StringUtils.isEmpty(req.getUserNo()) || StringUtils.isEmpty(req.getPwd())) {
                throw new MyRuntimeException(CodeMsgEnum.ACCOUNT_OR_PASSWORD_IS_INCORRECT.getMessage());
            }

            User = userMapper.findUserWithUserNo(req.getUserNo());
            if (null == User) {
                throw new MyRuntimeException(CodeMsgEnum.ACCOUNT_OR_PASSWORD_IS_INCORRECT.getMessage());
            }
            String passwordInDb = User.getPwd();
            if (!StringUtils.isEmpty(passwordInDb) && passwordInDb.startsWith("{bcrypt}")) {
                passwordInDb = passwordInDb.substring("{bcrypt}".length());
            }

            if (!new BCryptPasswordEncoder().matches(req.getPwd(), passwordInDb)) {
                throw new MyRuntimeException(CodeMsgEnum.ACCOUNT_OR_PASSWORD_IS_INCORRECT.getMessage());
            }

            MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
            paramMap.add("client_id", clientId);
            paramMap.add("client_secret", clientSecret);
            paramMap.add("username", req.getUserNo());
            paramMap.add("password", req.getPwd());
            paramMap.add("grant_type", "password");

            Token token = null;

            //因为oauth2本身自带的登录接口是"/oauth/token"，并且返回的数据类型不能按我们想要的去返回
            //但是我的业务需求是，登录接口是"user/login"，由于我没研究过要怎么去修改oauth2内部的endpoint配置
            //所以这里我用restTemplate(HTTP客户端)进行一次转发到oauth2内部的登录接口，比较简单粗暴
            token = restTemplate.postForObject(serverConfig.getUrl() + "/oauth/token", paramMap, Token.class);
            return token;
        } catch (MyRuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            throw new MyRuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public User getLoginUser() {
        Authentication authentication = authenticationFacade.getAuthentication();
        String userNo = authentication.getName();

        User sysTUser = this.userMapper.findUserWithUserNo(userNo);
        return sysTUser;
    }
}
